package com.aleyn.router.inject.instance

import com.aleyn.router.inject.qualifier.Qualifier
import java.util.concurrent.ConcurrentHashMap
import kotlin.reflect.KClass

/**
 * @author: Aleyn
 * @date: 2023/7/27 11:58
 *
 * 实例注册表。
 * 这是依赖注入容器的核心，负责存储、管理和解析所有已定义的实例工厂。
 */
@Suppress("UNCHECKED_CAST")
class InstanceRegistry {

    // 使用 ConcurrentHashMap 存储所有实例工厂，键是 `mappingKey` 生成的唯一标识
    private val _instances = ConcurrentHashMap<String, InstanceFactory<*>>()

    // 存储需要“急切”初始化的单例工厂
    private val eagerInstances = hashMapOf<Int, SingleInstanceFactory<*>>()

    /**
     * 添加一个“急切”初始化的单例工厂。
     *
     * @param factory 要添加的单例工厂。
     */
    fun addEagerInstances(factory: SingleInstanceFactory<*>) {
        eagerInstances[factory.hashCode()] = factory
    }

    /**
     * 创建所有被标记为“急切”的单例实例。
     * 通常在应用启动时调用一次。
     */
    internal fun createAllEagerInstances() {
        createEagerInstances(eagerInstances.values)
        eagerInstances.clear()
    }

    /**
     * 保存一个映射关系和对应的实例工厂。
     *
     * @param mapping 唯一映射键。
     * @param factory 实例工厂。
     * @throws error 如果映射键已存在，则抛出错误。
     */
    fun saveMapping(
        mapping: String,
        factory: InstanceFactory<*>
    ) {
        if (_instances.containsKey(mapping)) {
            error("A value cannot correspond to multiple types. Please check the following information:\n  ${factory.definition} \n at $mapping")
        }
        _instances[mapping] = factory
    }

    /**
     * 批量创建“急切”实例。
     */
    private fun createEagerInstances(eagerInstances: Collection<SingleInstanceFactory<*>>) {
        if (eagerInstances.isNotEmpty()) {
            eagerInstances.forEach { factory ->
                factory.get(InstanceData())
            }
        }
    }

    /**
     * 解析并返回一个实例。
     *
     * @param T 期望返回的实例类型。
     * @param qualifier 可选的限定符。
     * @param clazz 实例的 KClass。
     * @param instanceContext 实例上下文。
     * @return 解析出的实例，如果找不到则返回 null。
     */
    internal fun <T> resolveInstance(
        qualifier: Qualifier?,
        clazz: KClass<*>,
        instanceContext: InstanceData
    ): T? {
        val mappingKey = mappingKey(clazz, qualifier)
        return _instances[mappingKey]?.get(instanceContext) as? T
    }


    /**
     * 清空所有已注册的实例工厂。
     */
    internal fun close() {
        _instances.clear()
    }

    /**
     * 获取某个类型的所有已注册实例。
     *
     * @param T 期望返回的实例类型。
     * @param clazz 实例的 KClass。
     * @return 该类型的所有实例列表。
     */
    internal fun <T> getAll(clazz: KClass<*>): List<T> {
        return _instances.values
            .filter { factory ->
                factory.definition.primaryType == clazz
            }
            .distinct()
            .map { it.get(InstanceData()) as T }
    }
}